package pzc.reflect.introspection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 自省工具类.
 * <p>
 * 
 * <pre>
 * 访问类的实例成员，静态成员
 * 不使用setter和getter，私有成员也可以访问
 * 不依赖于任何第三方包
 * </pre>
 * 
 * @author 彭
 * @version 1.0.0 2009/08/24
 */
public class IntrospectionUtils {

	public final static boolean isExistInstanceField(Class<?> cls, String propertyName) {
		return (getInstanceField(cls, propertyName) != null);
	}

	public final static Field getInstanceField(Class<?> cls, String propertyName) {
		boolean isFound = false;
		Field ret = null;
		for (Class<?> clazz = cls; clazz != null; clazz = clazz.getSuperclass()) {
			Field[] fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				Field fe = fields[i];
				if (!Modifier.isStatic(fe.getModifiers()) && fe.getName().equalsIgnoreCase(propertyName)) {
					isFound = true;

					// important
					fe.setAccessible(true);

					ret = fe;
					break;
				}
			}

			if (isFound) {
				break;
			}
		}
		return ret;
	}

	public final static Object getInstanceProperty(Object obj, String propertyName) throws IllegalArgumentException, IllegalAccessException {
		Field fe = getInstanceField(obj.getClass(), propertyName);
		return fe.get(obj);
	}

	public final static void setInstanceProperty(Object obj, String propertyName, Object value) throws IllegalArgumentException, IllegalAccessException {
		Field fe = getInstanceField(obj.getClass(), propertyName);
		fe.set(obj, value);
	}

	public final static Field getStaticField(Class<?> cls, String propertyName) throws SecurityException, NoSuchFieldException {
		// static field can not be inherited
		Field fe = cls.getDeclaredField(propertyName);
		if (Modifier.isStatic(fe.getModifiers())) {
			// important
			fe.setAccessible(true);
			return fe;
		} else {
			return null;
		}
	}

	public final static Object getStaticProperty(Class<?> cls, String propertyName) throws IllegalArgumentException, SecurityException, IllegalAccessException,
			NoSuchFieldException {
		return getStaticField(cls, propertyName).get(null);
	}

	public final static void setStaticProperty(Class<?> cls, String propertyName, Object value) throws SecurityException, NoSuchFieldException,
			IllegalArgumentException, IllegalAccessException {
		getStaticField(cls, propertyName).set(null, value);
	}

	public final static Method getMethod(Class<?> cls, String methodName, Class<?>[] parameterTypes, boolean isStatic) {
		if (parameterTypes == null) {
			parameterTypes = new Class<?>[] {};
		}

		boolean isFound = false;
		Method retMtd = null;
		for (Class<?> clazz = cls; clazz != null; clazz = clazz.getSuperclass()) {
			Method[] methods = clazz.getDeclaredMethods();
			for (int i = 0; i < methods.length; i++) {
				Method mtd = methods[i];
				Class<?>[] mtdParameterTypes = mtd.getParameterTypes();
				if (mtd.getName().equalsIgnoreCase(methodName)) {
					if (mtdParameterTypes.length == parameterTypes.length) {
						boolean sameParams = true;
						for (int k = 0; k < mtdParameterTypes.length; k++) {
							Class<?> cls1 = mtdParameterTypes[k];
							Class<?> cls2 = parameterTypes[k];
							if (!cls1.equals(cls2)) {
								sameParams = false;
								break;
							}
						}
						if (sameParams) {
							if ((isStatic == true && Modifier.isStatic(mtd.getModifiers())) || (isStatic == false && !Modifier.isStatic(mtd.getModifiers()))) {
								isFound = true;
								retMtd = mtd;
								// important
								retMtd.setAccessible(true);
								break;
							}
						}
					}
				}
			}

			if (isFound) {
				break;
			}
		}
		return retMtd;
	}

	public final static Object invokeInstanceMethod(Object obj, String methodName, Class<?>[] parameterTypes, Object[] args) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException {
		Object ret = null;
		Method mtd = getMethod(obj.getClass(), methodName, parameterTypes, false);
		ret = mtd.invoke(obj, args);
		return ret;
	}

	public final static Object invokeInstanceMethod(Object obj, String methodName) throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {
		return invokeInstanceMethod(obj, methodName, null, null);
	}

	public final static Object invokeStaticMethod(Class<?> cls, String methodName, Class<?>[] parameterTypes, Object[] args) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException {
		Object ret = null;
		Method mtd = getMethod(cls, methodName, parameterTypes, true);
		ret = mtd.invoke(null, args);
		return ret;
	}

	public final static Object invokeStaticMethod(Class<?> cls, String methodName) throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException {
		return invokeStaticMethod(cls, methodName, null, null);
	}

	public final static <T> T invokeConstructor(Class<T> objCls, Class<?>[] parametersCls, Object[] args) throws SecurityException, NoSuchMethodException,
			IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
		T ret = null;
		Constructor<T> method = objCls.getDeclaredConstructor(parametersCls);

		// important
		method.setAccessible(true);

		ret = method.newInstance(args);
		return ret;
	}

	/**
	 * Call the default constructor(not parameters).
	 * 
	 * @param <T>
	 * @param objCls
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public final static <T> T invokeConstructor(Class<T> objCls) throws SecurityException, NoSuchMethodException, IllegalArgumentException,
			InstantiationException, IllegalAccessException, InvocationTargetException {
		return invokeConstructor(objCls, null, null);
	}

	public final static <T extends Annotation> T getFieldAnnotation(Class<?> cls, String propertyName, Class<T> annotationCls) {
		Field field = IntrospectionUtils.getInstanceField(cls, propertyName);
		T annotation = field.getAnnotation(annotationCls);
//		T annotation = (T)field.getDeclaredAnnotations()[0];
		return annotation;
	}
}
